#include <unistd.h>
#include <stdlib.h>

#include <iostream>
#include <string>
#include <vector>

#include <libxml/tree.h>
#include <libxml/parser.h>
#include <libxml/HTMLparser.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>

#include <boost/shared_ptr.hpp>
#include <boost/bind/bind.hpp>
#include <boost/typeof/typeof.hpp>

int execute_xpath_expression( const char *filename, const std::vector< std::string > &xpaths );
int execute_relative_xpath_expression( xmlNodeSetPtr node_set,  xmlXPathContextPtr xpath_ctx, const std::vector< std::string > &xpaths, size_t index );
void print_xml_node( xmlNodePtr node );

int main( int argc, char *argv[] )
{
	if( argc < 3 ) {
		std::cerr << argv[0] << " file xpath [xpath ...]" << std::endl;
	}
	std::vector< std::string > xpaths;
	for( int i = 2; i < argc; ++i ) {
		xpaths.push_back( argv[i] );
	}
	return execute_xpath_expression( argv[1], xpaths );
}

int execute_xpath_expression( const char *filename, const std::vector< std::string > &xpaths )
{
	assert( !xpaths.empty() );

	htmlDocPtr doc = htmlReadFile( filename, NULL, HTML_PARSE_RECOVER | HTML_PARSE_NOERROR |
				HTML_PARSE_NOWARNING | HTML_PARSE_PEDANTIC |
				HTML_PARSE_NOBLANKS | HTML_PARSE_NONET );
	if( NULL == doc ) {
		return -1;
	}
	boost::shared_ptr< void > doc_guard( (void *)NULL, boost::bind( xmlFreeDoc, doc ) );
	xmlXPathContextPtr xpath_ctx = xmlXPathNewContext( doc );
	if( NULL == xpath_ctx ) {
		return -1;
	}
	boost::shared_ptr< void > xpath_ctx_guard( (void *)NULL, boost::bind( xmlXPathFreeContext, xpath_ctx ) );

	xmlXPathObjectPtr object = xmlXPathEvalExpression( BAD_CAST xpaths.at(0).c_str(), xpath_ctx );
	if( NULL == object ) {
		std::cerr << "Error: unable to evaluate xpath expression: " << xpaths.at(0) << std::endl;
	}
	boost::shared_ptr< void > object_guard( (void *)NULL, boost::bind( xmlXPathFreeObject, object ) );

	return execute_relative_xpath_expression( object->nodesetval, xpath_ctx, xpaths, 1 );
}

void print_xml_node( xmlNodePtr node )
{
	if( XML_ELEMENT_NODE == node->type ) {
		std::cout << "element node: " << (const char *)node->name << std::endl;
	}
	else if ( XML_ATTRIBUTE_NODE == node->type ) {
		std::cout << "attribute node, name: " << (const char *)node->name 
			<< " value: " << (const char *)(NULL != node->children && NULL != node->children->content ? node->children->content: NULL ) 
			<< std::endl;
	}
	else {
		std::cout << "node type: " << node->type << " name: " << (const char *)(node->name ? node->name : NULL ) << std::endl;
	}
	return;
}

int execute_relative_xpath_expression( xmlNodeSetPtr node_set,  xmlXPathContextPtr xpath_ctx, const std::vector< std::string > &xpaths, size_t index )
{
	int size = NULL == node_set ? 0 : node_set->nodeNr;
	for( int i = 0; i < size; i++ ) {
		if( xpaths.size() == index ) {
			print_xml_node( node_set->nodeTab[i] );
		}
		else {
			xpath_ctx->node = node_set->nodeTab[i];
			xmlXPathObjectPtr object = xmlXPathEvalExpression( BAD_CAST xpaths.at(index).c_str(), xpath_ctx );
			boost::shared_ptr< void > object_guard( (void *)NULL, boost::bind( xmlXPathFreeObject, object ) );
			if( NULL == object ) {
				std::cerr << "Error: unable to evaluate xpath expression: " << xpaths.at(index) << std::endl;
			}
			int rc = execute_relative_xpath_expression( object->nodesetval, xpath_ctx, xpaths, index+1 );
			if( 0 != rc )
				return -1;
		}
	}

	return 0;
}

